home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / oper_sys / mach / amiga / scsi9091.lzh / scsi_cmd.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-02-24  |  14.5 KB  |  532 lines

  1. #include <exec/types.h>
  2. #include <exec/nodes.h>
  3. #include <exec/resident.h>
  4. #include <exec/errors.h>
  5. #include <libraries/expansionbase.h>
  6. #include <libraries/configvars.h>
  7. #include <devices/trackdisk.h>
  8.  
  9. /*
  10.  * Set SysBase to a local variable, that loads directly from 4 when it
  11.  * has to be reloaded
  12.  */
  13. #define BASE_EXT_DECL
  14. #define BASE_NAME (*(void **)4)
  15. #include <inline/exec.h>
  16.  
  17. /*
  18.  * This is the main scsi-command executor task. It does the whole
  19.  * scsi-communication and encapsulates hardware usage.
  20.  */
  21.  
  22. extern struct MsgPort *CreatePort(char *name, int pri);
  23. extern void DeletePort(struct MsgPort *mp);
  24.  
  25. #include "device.h"
  26.  
  27. DECL_DPRINTF;
  28. extern void install_int_handler (struct scsi_dev *dev);
  29. extern void remove_int_handler (struct scsi_dev *dev);
  30. extern void wait_interrupt (struct scsi_dev *dev);
  31. extern void clear_interrupt (struct scsi_dev *dev);
  32.  
  33. void scsi_cmd (struct scsi_dev *dev);
  34. void init_board (struct scsi_dev *dev);
  35.  
  36. /* this function is used by the int_handler, the rest is not */
  37. int read_scsi_reg (volatile ubyte *board, int reg_num);
  38. static void inline read_scsi_regs (volatile ubyte *board, int reg_start, 
  39.                    ubyte num, ubyte *buf);
  40. static void inline write_scsi_regs (volatile ubyte *board, int reg_start, 
  41.                     ubyte num, ubyte *buf);
  42. static void set_dma_state (volatile ubyte *board, int state, int value);
  43. static void write_scsi_reg (volatile ubyte *board, int reg_num, int value);
  44. static int poll_interrupts (struct scsi_dev *dev, int *status, int length);
  45. static void load_dma_address (volatile ubyte *board, int direction, 
  46.                   void *address);
  47. static int get_cmd_len (int cmd);
  48. static int checkfifo_n_dmareset (volatile ubyte *board);
  49.  
  50. /*
  51.  * reset states to int-enable & scsi mode
  52.  */
  53. #ifdef A2090
  54. #define REINIT_DMA_CIRCUIT() board[A2090_CNTL_HI] = MRESET|SEL_SCSI|INT_ENABLE 
  55. #endif
  56. #ifdef A2091
  57. #define REINIT_DMA_CIRCUIT()
  58. #endif
  59.  
  60. /***********************************************************************/
  61.  
  62. /* this is the handler task, that gets command messages, executes them
  63.  * and replies to them, when they are finished */
  64.  
  65. void
  66. scsi_cmd (struct scsi_dev *dev)
  67. {
  68.   struct scsi_msg *sm;
  69.   struct MsgPort *mp;
  70.   int i, cmd_len, status;
  71.   volatile ubyte *board = (ubyte *)dev->sc_base;
  72.   ulong length;
  73.  
  74. DPRINTF(("handler-task: dev = $%lx, board = $%lx", dev, board));
  75.  
  76.   /* set up our interrupt handler */
  77.   install_int_handler (dev);
  78.  
  79.   /* put the board into a defined state */
  80.   init_board (dev);
  81.  
  82.   /* for now, just assume it worked... */
  83.   mp = CreatePort("scsi_cmd.port",0);
  84.   dev->sc_hmsgport = mp;
  85.   PutMsg(&dev->sc_msgport, (struct Message *)&dev->sc_hmessage);
  86.   
  87.   for (;;)
  88.     {
  89.       WaitPort(mp);
  90.       while (sm = (struct scsi_msg *) GetMsg(mp))
  91.     {
  92. DPRINTF(("handler-task(%ld): cmd: %lx, scsi-cmd: %lx",
  93.      sm->scm_unit, sm->scm_cmd, sm->scm_scsi_cmd->scsi_Command[0]));
  94.  
  95.       if (sm->scm_cmd == SCM_CMD_SHUTDOWN)
  96.         goto shutdown;
  97.       else if (sm->scm_cmd == SCM_CMD_EXEC_SCSI)
  98.         {
  99.           /*
  100.            * first check, that we won't try to send a command
  101.            * to the controller: 
  102.            */
  103.           if (sm->scm_unit == 7)
  104.         {
  105.           sm->scm_cmd = HFERR_SelfUnit;
  106.           ReplyMsg((struct Message*)sm);
  107.           continue;
  108.         }
  109.  
  110.           REINIT_DMA_CIRCUIT();
  111.  
  112.           /* since the user can specify an scsi_Length of up to 
  113.            * 2**24 byte, and the 2090-DMA circuit only stands
  114.            * 64K, I'll limit any given Length to these 64K .. */
  115.           length = sm->scm_scsi_cmd->scsi_Length;
  116.           if (length > DMA_MAX_TRANSFER) length = DMA_MAX_TRANSFER;
  117.  
  118.           /* both, the 2090 and the 2091 can't handle more than
  119.            * 24bit addresses as DMA target */
  120.           if (length && (ulong)sm->scm_scsi_cmd->scsi_Data >= 0x01000000)
  121.         AutoAutoRequest(SCSI_NAME,
  122.                 "Your DMA target is above $1000000",
  123.                 "This is not supported, this transfer will fail!",
  124.                 "Continue", "Continue");
  125.  
  126.           /* execute the SCSICommand attached to the message */
  127.           if (length)
  128.         load_dma_address (board,
  129.                   sm->scm_scsi_cmd->scsi_Flags & SCSIF_READ,
  130.                   sm->scm_scsi_cmd->scsi_Data);
  131.  
  132.           /* 
  133.            * copy the CDB (ignoring scsi_CmdLength... an SCSI command
  134.            * has a fixed length, determined from its group code) 
  135.            */
  136.           cmd_len = get_cmd_len (sm->scm_scsi_cmd->scsi_Command[0]);
  137.           write_scsi_regs (board, WD_CDB_1, cmd_len,
  138.                    sm->scm_scsi_cmd->scsi_Command);
  139.           sm->scm_scsi_cmd->scsi_CmdActual = cmd_len;
  140.           
  141.           /* set transfer length in WD chip, DMA is already ok */
  142.           write_scsi_regs (board, WD_TRANSFER_COUNT_MSB, 3,
  143.                          ((ubyte *)&length)+1);
  144.  
  145.           write_scsi_reg (board, WD_DESTINATION_ID, sm->scm_unit);
  146.  
  147.           clear_interrupt (dev);
  148.           write_scsi_reg (board, WD_COMMAND, 
  149.                   WD_CMD_SELECT_WITHOUT_ATN_AND_TRANSFER);
  150.  
  151.           /*
  152.            * this field serves two purposes, when the message is sent,
  153.            * it is used as a global command, when the message is
  154.            * returned, it contains the io_Error value 
  155.            */
  156.           sm->scm_cmd = poll_interrupts (dev, &status, length);
  157.  
  158.           /*
  159.            * get the value of the transfer counter after the execution
  160.            * of the command
  161.            */
  162.           cmd_len = 0;
  163.           read_scsi_regs (board, WD_TRANSFER_COUNT_MSB, 3,
  164.                          ((ubyte *)&cmd_len)+1);
  165.  
  166.           /* and deduce the actual transferred amount of data */
  167.           sm->scm_scsi_cmd->scsi_Actual = length - cmd_len;
  168.  
  169.           sm->scm_scsi_cmd->scsi_Status = status;
  170.           /*
  171.            * if CHECK_CONDITION is set, and the user wanted to
  172.            * AUTOSENSE, do a REQUEST_SENSE now 
  173.            */
  174.           if ((status & 2) == 2 &&
  175.           (sm->scm_scsi_cmd->scsi_Flags & SCSIF_AUTOSENSE))
  176.         {
  177. DPRINTF(("handler-task(%ld): need autosense information", sm->scm_unit));
  178.           load_dma_address (board,
  179.                     DIRECTION_READ,
  180.                     sm->scm_scsi_cmd->scsi_SenseData);
  181.           /* REQUEST_SENSE */
  182.           write_scsi_regs (board, WD_CDB_1, 4, (ubyte*)"\3\0\0\0");
  183.           if (sm->scm_scsi_cmd->scsi_Flags & SCSIF_OLDAUTOSENSE)
  184.             cmd_len = 4;
  185.           else
  186.             cmd_len = sm->scm_scsi_cmd->scsi_SenseLength;
  187.            write_scsi_reg (board, WD_CDB_5, 
  188.             (sm->scm_scsi_cmd->scsi_Flags & SCSIF_OLDAUTOSENSE) ?
  189.             0 : cmd_len);
  190.            write_scsi_reg (board, WD_CDB_6, 0);
  191.  
  192.               write_scsi_regs (board, WD_TRANSFER_COUNT_MSB, 3,
  193.                          ((ubyte *)&cmd_len)+1);
  194.  
  195.           /* needed the second time ?? */
  196.           write_scsi_reg (board, WD_DESTINATION_ID, sm->scm_unit);
  197.           
  198.           clear_interrupt (dev);
  199.               write_scsi_reg (board, WD_COMMAND, 
  200.                   WD_CMD_SELECT_WITHOUT_ATN_AND_TRANSFER);
  201.  
  202.           poll_interrupts (dev, &status, cmd_len);
  203.           
  204.               cmd_len = 0;
  205.               read_scsi_regs (board, WD_TRANSFER_COUNT_MSB, 3,
  206.                             ((ubyte *)&cmd_len)+1);
  207.           sm->scm_scsi_cmd->scsi_SenseActual =
  208.             sm->scm_scsi_cmd->scsi_SenseLength - cmd_len;
  209. DPRINTF(("\013handler-task(%ld): autosense information = $%lx", 
  210.     sm->scm_unit, *(ulong *)sm->scm_scsi_cmd->scsi_SenseData));
  211.         }
  212.           ReplyMsg ((struct Message*)sm);
  213.         }
  214.     }
  215.     }
  216.  
  217. shutdown :
  218.   Forbid ();
  219.   ReplyMsg ((struct Message *)sm);
  220.   DeletePort (mp);
  221.   remove_int_handler (dev);
  222.   RemTask (FindTask (0));
  223. }
  224.  
  225. /*
  226.  * IMPORTANT: not all HFERR_ that could be detected are reported. This is
  227.  * to try to ensure, that the bus is always in a defined state, when this
  228.  * function returns. Only a timeout-condition on select and a disconnect
  229.  * will cause this function to return. 
  230.  */
  231.  
  232. int
  233. poll_interrupts (struct scsi_dev *dev, int *cmd_status, int length)
  234. {
  235.   int status;
  236.   int do_wait = 1; /* only needed after a command has been started */
  237.   volatile ubyte *board = (volatile ubyte *)dev->sc_base;
  238.   
  239.   for (;;)
  240.     {
  241.       if (do_wait) 
  242.         {
  243.       wait_interrupt (dev);
  244.       do_wait = 0;
  245.     }
  246.  
  247.       status = read_scsi_reg (board, WD_SCSI_STATUS);
  248.  
  249.       switch (status)
  250.     {
  251.     case 0x00: /* after RESET */
  252.     case 0x16: /* COMMAND COMPLETED */
  253.       break;
  254.     case 0x1f: /* INFO OUT COMPLETED */
  255.       do_wait = 1;
  256.       clear_interrupt (dev);
  257.       write_scsi_reg (board, WD_COMMAND,
  258.               WD_CMD_TRANSFER_INFO|WD_CMD_SBT_MODE);
  259.       while (! (read_scsi_reg (board, WD_AUXILIARY_STATUS) & 1)) ;
  260.       read_scsi_reg (board, WD_DATA);
  261.       break;
  262.     case 0x20:
  263.       do_wait = 1;
  264.       clear_interrupt (dev);
  265.       write_scsi_reg (board, WD_COMMAND, WD_CMD_NEGATE_ACK);
  266.       break;
  267.     case 0x42: /* SELECT TIMEOUT */
  268.       if (length) checkfifo_n_dmareset (board);
  269.       return HFERR_SelTimeout;
  270.     case 0x4b: /* requested unusual info out */
  271.       do_wait = 1;
  272.       clear_interrupt (dev);
  273.       write_scsi_reg (board, WD_COMMAND,
  274.               WD_CMD_TRANSFER_INFO|WD_CMD_SBT_MODE);
  275.       while (! (read_scsi_reg (board, WD_AUXILIARY_STATUS) & 1)) ;
  276.       *cmd_status = read_scsi_reg (board, WD_DATA);
  277.       /*
  278.        * no (!) return here... the old 2090 device used to return
  279.        * in such conditions, leaving a device in a connected state,
  280.        * that would later timeout and leave the bus in a 
  281.        * broken state..
  282.        */
  283.       break;
  284.     default:
  285.       /* printf("strange-status: $%x\n", status); */
  286.       break;
  287.     case 0x85: /* DISCONNECT */
  288.       if (!length || checkfifo_n_dmareset (board))
  289.         {
  290.           /* 0x60 == COMMAND_COMPLETED PHASE */
  291.           if (read_scsi_reg (board, WD_COMMAND_PHASE) == 0x60)
  292.         {
  293.           *cmd_status = read_scsi_reg (board, WD_TARGET_LUN);
  294.           return *cmd_status ? HFERR_BadStatus : 0;
  295.         }
  296.           else
  297.         return HFERR_Phase;
  298.         }
  299.       else
  300.         return HFERR_DMA;
  301.     }
  302.     }
  303. }
  304.  
  305. void
  306. init_board (struct scsi_dev *dev)
  307. {
  308.   volatile ubyte *board = (volatile ubyte *)dev->sc_base;
  309.  
  310. #ifdef A2090
  311.   /* ST506 init stuff... shudder.. */
  312.  
  313.   int i;
  314.   int try;
  315.   ulong foo_addr = 0x79000; /* aehm... */
  316.  
  317.   /* from 2620 monitor.. hope it works:-)) */
  318.   for (i = 0; i < 2; i++)
  319.     {
  320.       try = 2500000;
  321. DPRINTF(("st506: 1part"))
  322.       *(unsigned short *)&board[0x50] = foo_addr >> (1 + (i==1?8:0));
  323.       board[A2090_CNTL_HI] = MRESET|HCBP;  /* 506 stuff... */
  324.       while (!(board[A2090_CNTL_HI] & (1<<7)) && try-- > 0) ;
  325.       try = 2500000;
  326. DPRINTF(("st506: 2part"))
  327.       board[A2090_CNTL_HI] = MRESET;
  328.       while ((board[A2090_CNTL_HI] & (1<<7)) && try-- > 0) ;
  329.     }
  330.  
  331.   board[A2090_CNTL_HI] = MRESET|SEL_SCSI;
  332. #endif
  333. #ifdef A3000
  334.   board[0x0b] = 0;
  335.   board[0x3f] = 0;
  336.   board[0x03] = 0;
  337.   board[0x1b] = 0;
  338.   board[0x0b] = 12;  /* what this might do..... */
  339. #endif
  340.  
  341.   read_scsi_reg (board, WD_SCSI_STATUS);
  342.  
  343.   REINIT_DMA_CIRCUIT();
  344.   
  345.   write_scsi_reg (board, WD_OWN_ID, 7);
  346.   write_scsi_reg (board, WD_COMMAND, WD_CMD_RESET);
  347.  
  348. DPRINTF(("init: wait_interrupt"))
  349.   wait_interrupt (dev);
  350.   
  351. DPRINTF(("init: set controller into dma mode"))
  352.   do
  353.     write_scsi_reg (board, WD_CONTROL, 0x80); /* set chip into DMA mode */
  354.   while (read_scsi_reg (board, WD_CONTROL) != 0x80);
  355.  
  356. /*  write_scsi_reg (board, WD_TIMEOUT_PERIOD, 0xff); /* timeout ~~2s */
  357.   write_scsi_reg (board, WD_TIMEOUT_PERIOD, 0x20); /* timeout minimal */
  358.  
  359.   read_scsi_reg (board, WD_SCSI_STATUS);
  360.  
  361.   write_scsi_reg (board, WD_SYNCHRONOUS_TRANSFER, 0x40); /* no sync */
  362.  
  363. #ifdef A2090
  364.   checkfifo_n_dmareset (board);
  365. #endif
  366. }
  367.  
  368. /***********************************************************************/
  369.  
  370. static int
  371. get_cmd_len (int cmd)
  372. {
  373.   switch (cmd >> 5)
  374.     {
  375.     case 0: return 6;
  376.     case 1: return 10;
  377.     default: /* assume maximum */
  378.     case 5: return 12;
  379.     }
  380. }
  381.  
  382. static void
  383. load_dma_address (volatile ubyte *board, int direction, void *address)
  384. {
  385. #ifdef A2090
  386.   ulong data;
  387.   
  388.   data = (ulong)address >> 1;
  389.   if (direction == DIRECTION_WRITE) data |= 0x800000;
  390.   set_dma_state (board, DMA_LD_UP_ADDR, data >> 16);
  391.   set_dma_state (board, DMA_LD_MID_ADDR, data >> 8);
  392.   set_dma_state (board, DMA_LD_LO_ADDR, data);
  393.   board[SCSI_PCSS] = DMA_OPEN_FIFO;
  394. #endif
  395. #ifdef A2091
  396.   if (direction == DIRECTION_WRITE)
  397.     *(ushort *)&board[0x42] = 0x38;
  398.   else
  399.     *(ushort *)&board[0x42] = 0x30;
  400.   
  401.   *(ulong *)&board[0x80] = 0;
  402.   *(ulong *)&board[0x84] = (ulong) address;
  403.   *(ushort *)&board[0xe0] = 1;
  404. #endif
  405. #ifdef A3000
  406.   if (direction == DIRECTION_WRITE)
  407.     *(ushort *)&board[0x42] = 0x38;
  408.   else
  409.     *(ushort *)&board[0x42] = 0x30;
  410.   
  411.   *(ulong *)&board[0x80] = 0;
  412.   *(ulong *)&board[0x0c] = (ulong) address;
  413.   *(ushort *)&board[0xe0] = 1;
  414. #endif
  415. }
  416.  
  417. int
  418. read_scsi_reg (volatile ubyte *board, int reg_num)
  419. {
  420.   int result;
  421.  
  422.   /* need to ensure, that register access is single threaded, the
  423.    * board-interrupt will also try to use read_scsi_reg () .. */
  424.   Disable();
  425.   board[SCSI_CS_CNTL] = reg_num;
  426.   result = board[SCSI_CS_DATA];
  427.   Enable();
  428.   return result;
  429. }
  430.  
  431. static void inline
  432. read_scsi_regs (volatile ubyte *board, int reg_start, ubyte num, ubyte *buf)
  433. {
  434.   /* need to ensure, that register access is single threaded, the
  435.    * board-interrupt will also try to read those registers */
  436.   Disable();
  437.   board[SCSI_CS_CNTL] = reg_start;
  438.   while (num --) *buf++ = board[SCSI_CS_DATA];
  439.   Enable();
  440. }
  441.  
  442. static void inline
  443. write_scsi_regs (volatile ubyte *board, int reg_start, ubyte num, ubyte *buf)
  444. {
  445.   /* need to ensure, that register access is single threaded, the
  446.    * board-interrupt will also try to read those registers */
  447.   Disable();
  448.   board[SCSI_CS_CNTL] = reg_start;
  449.   while (num --) board[SCSI_CS_DATA] = *buf++;
  450.   Enable();
  451. }
  452.  
  453. static void
  454. write_scsi_reg (volatile ubyte *board, int reg_num, int value)
  455. {
  456.   Disable();
  457.   board[SCSI_CS_CNTL] = reg_num;
  458.   board[SCSI_CS_DATA] = value;
  459.   Enable();
  460. }
  461.  
  462. static int
  463. checkfifo_n_dmareset (volatile ubyte *board)
  464. {
  465. #ifdef A2090
  466.   ubyte i;
  467.   int status;
  468.  
  469.   board[SCSI_PCSS] = DMA_READ_STATUS;
  470.   for (i = 0; i <= 10; i++) ;
  471.   status = board[SCSI_PCSD];
  472.   for (i = 0; i <= 10; i++) ;
  473.   board[SCSI_PCSS] = DMA_RESET_1;
  474.   for (i = 0; i <= 10; i++) ;
  475.   board[SCSI_PCSS] = DMA_RESET_2;  
  476.   for (i = 0; i <= 10; i++) ;
  477.  
  478.   return status & 0x20;
  479. #endif
  480. #ifdef A2091
  481.   *(ushort *)&board[0xe8] = 1;
  482.   while (!(board[0x41] & 1)) ;
  483.   *(ushort *)&board[0xe4] = 1;
  484.   *(ushort *)&board[0xe2] = 1;
  485.   return 1; /* there seem to be no possible DMA errors.. GREAT!! */
  486. #endif  
  487. }
  488.  
  489. #ifdef A2090
  490. static void
  491. set_dma_state (volatile ubyte *board, int state, int addr)
  492. {
  493.   ubyte i;
  494.  
  495.   board[SCSI_PCSS] = state;
  496.   /* busy loop */
  497.   for (i = 0; i <= 10; i++) ;
  498.   
  499.   board[SCSI_PCSD] = addr;
  500.   /* busy loop */
  501.   for (i = 0; i <= 10; i++) ;
  502. }
  503. #endif
  504.  
  505. /***********************************************************************/
  506. /***********************************************************************/
  507.  
  508. #ifdef NEED_DUMP_REGS
  509. void
  510. dump_regs(char *title)
  511. {
  512.   ubyte *board = get_board_address((int *)board);
  513.  
  514.   printf("%s: tgt-lun = $%2x, cmd-ph = $%2x, sync = $%2x,\nsrc-id = $%2x, stat = $%x, data = $%2x, aux = $%2x\n",
  515.     title,
  516.     read_scsi_reg(board, WD_TARGET_LUN),
  517.     read_scsi_reg(board, WD_COMMAND_PHASE),
  518.     read_scsi_reg(board, WD_SYNCHRONOUS_TRANSFER),
  519.     read_scsi_reg(board, WD_SOURCE_ID),
  520.     read_scsi_reg(board, WD_SCSI_STATUS),
  521.     read_scsi_reg(board, WD_DATA),
  522.     read_scsi_reg(board, WD_AUXILIARY_STATUS));
  523.   /* wait for middle mouse button to be released */
  524.   while (!(*(unsigned short *)0xdff016 & (1 << 8))) ;
  525. }
  526.  
  527. void
  528. init_controller (ubyte *board)
  529. {
  530. }
  531. #endif
  532.